Instruction Primer
In order to write programs in 6502 assembly language a considerable amount of seemingly
unrelated background knowledge is required. The major purpose behind the SPEED/ASM package
is to reduce the amount of that knowledge necessary to write useful programs.

Mind you I said reduce, not eliminate. With this third instalment of my column I will
finish up most of the necessary background material so you can begin writing reasonable
machine language programs. This article should also help tie up loose ends from the first
two articles.

An Introduction to 6502 Assembly Language
You cannot properly program using SPEED/ASM without at least a little understanding of
6502 machine code. SPEED/ASM’s purpose, as stated above, is not to eliminate the need for
machine code, but to make the more difficult tasks easier. With this thought in mind it’s
time to introduce you to the simpler 6502 instructions.

The LDA Instruction
The LDA (load accumulator) instruction is easily the most used instruction on the 6502.
It copies the contents of one of the 65,535 memory locations the 6502 can address into a
special memory location called the accumulator.

This memory location is found inside the 6502 chip. Storage cells located inside the
processor chip are usually called registers.

The accumulator in the 6502 is where most of the action takes place.

Numbers are added together in the accumulator, strings are compared using the accumulator,
logical operations are performed by the accumulator, and more.

Typically, a value is loaded into the accumulator using the LDA instruction and then
that data is operated on using one of the 6502’s arithmetic or logical operations.

Consequently, the LDA instruction is usually the first instruction of any computational
sequence that is executed.

There are several ways the accumulator can be loaded: with a constant, from a variable,
from a string, or indirectly through a pointer. For the time being we will concern
ourselves with the first two addressing modes, loading the accumulator with a constant
and loading it from a variable.

Constants are specified by prefacing them with the pound sign, #.

For example,

      to load the accumulator with 55 you would use the instruction:

      LDA #55

There is one limitation on a constant you load into the 6502 accumulator — it cannot be
greater than 255 or less than 0.

The range limitation is due to the 8-bit size of the 6502 accumulator.

Attempting to load a value larger than 255 will yield the result <value> MOD 256.

For example:

   START LDA #“C”
         STA CHR
         JSR LOAD
         ADR 0,INT

   ; Print the character onto the screen as an ASCII character

         LDA CHR
         JSR PUTC

   ; Transfer the character to INT and print its ASCII code as a decimal value.

         LDA CHR
         STA INT
         JSR PRTINT
         ADR INT

   ; Load CHR with the value 204 and then print CHR on the screen as an ASCII character.

         LDA #204
         STA CHR
         LDA CHR
         JSR PUTC

Listing 1, SPEED/ASM program segment demonstrating the relationship between characters
           and their standard ASCII codes.


   EXIT   EQU  $FF59
   READLP JSR GETC
          CMP #CR          ;Constant declared in SPEED/ASM.EQUATES.
          BEQ ALLDONE
          JSR PUTC
          JMP READLP
ALLDONE   JMP EXIT         ;Return to Apple monitor.

Listing 2. Sample application of BEQ branch instruction.


   ; To emulate the statement
   ;

   ; C$ = CHR$(1)

      LDA I
      ORA  #$80   ;Set the H.O. bit to one.
      STA C

   ; To emulate the statement

   ; I = ASC(C$)

      LDA C
      AND #$7F   ;Set H.O. bit to zero.
      STA I
      LDA #0 ;Clear the H.O. byte of the
      STA I+1    ;integer variable.

Listing 3. Code sequences to implement Applesoft CHR$ and ASC instructions.
           Applesoft utilizes a non-standard 0-127 range for its ASCII codes.

   LDA #305

loads the accumulator with 49 (305 MOD 256 is 49).

If you need to handle numbers too large for 8 bits you have to split the operation into
two segments, the first handling the low order byte of the computation and the second
handling computation of the high order bytes.

The integer values used by the SPEED/ASM package require 2 bytes to represent values in
the range 0 to 65535 or (more commonly) signed values in the range — 32768 to 32767.

The pound sign operator lets you load the low order byte of a constant into the accumulator.

To get the high order byte of a constant into the accumulator the slash, /, operator is used.

The instruction:

   LDA /305

loads the high order byte of 305 (which is 1) into the accumulator.

To load a negative decimal value into the 6502 accumulator preface the negative number
with an exclamation point (LISA 2.5 users only).

For example,
   to load the accumulator with the low order byte of —465 you would use the instruction:

   LDA #! -465
To load the accumulator with the high order byte of —465 the instruction:

   LDA /! -465

is used.

To load a character constant into the 6502 accumulator follow the pound sign with the
character you wish to load, enclosed by quotes.

To load the character c into the accumulator you could use the instruction:

   LDA #"c”

Do not use a slash — that will always put 0 into the accumulator.

Normally a character is enclosed by quote marks.

If you enclose it in apostrophes the high order bit of the character will be set to 0.
Since the Apple normally likes its characters to have the high order bit set to 1 you
should always enclose a character in quotes.

Next to loading a constant into the accumulator, loading the contents of a memory location
is the most important function.

To load the accumulator with the contents of a memory location specify the address of that
location after the LDA instruction.

To load the low order byte of the variable I into the accumulator you would use the instruction:

   LDA I

If you inadvertently type a pound sign in front of I, the low order byte of the address
of I will be loaded into the accumulator.

Likewise, prefacing the I with a slash loads the high order byte of the address of I into
the accumulator.

If you want to load the high order byte of the variable I into the accumulator the instruction:

   LDA I + 1
is used.

The STA Instruction
The second most popular instruction is STA.
It stores a copy of the accumulator into a memory location.
To use the STA instruction follow STA with the address of the variable you wish to store
the accumulator into.

For example the instruction:

   STA I

stores the contents of the accumulator into the low order byte of variable I.
If I is an integer variable, you can store the accumulator into the high order byte using
the instruction:

  STA I + 1

The LDA and STA instructions can be combined to move data around in memory.
The SPEED/ASM MOVE subroutine copies a 2-byte integer value from one variable to another.

The calling sequence for MOVE is:

      JSR MOVE
      ADR VAR1,VAR2

MOVE transfers the 2 bytes at address
VARI to the 2 bytes at address VAR2.

This action is easily simulated using the 6502 assembly sequence:

      LDA VAR1
      STA VAR2
      LDA VAR1 + 1
      STA VAR2 + 1

This short piece of 6502 code loads the 2 bytes at addresses VAR1 and VAR1+1 and stores
them at addresses VAR2 and VAR2+1.

Note that the accumulator contains the value in VAR1+1 (and VAR2+1) at the end of this
code sequence.

Incidentally, the LDA/STA sequence above doesn’t exactly duplicate the operation of the
MOVE subroutine. A call to MOVE requires only 7 bytes of program memory space, while the
LDA/STA sequence uses up 12 bytes. Furthermore, MOVE doesn’t affect the contents of the
6502 accumulator, whereas the LDA/STA sequence does. Whatever value the accumulator
contained before the execution of the sequence is lost, replaced by the same value as
VAR1+1.

Finally, MOVE executes quite a bit more slowly than the straight LDA/STA sequence.

If speed is the overriding consideration and the call to MOVE is deeply buried within a
loop, you should recode the JSR MOVE instruction using the LDA/STA sequence.

For most purposes, however, the call to MOVE is better since it is shorter and it doesn’t
affect the accumulator. The speed difference is usually insignificant, unless, as mentioned,
MOVE is buried deep inside nested loops. After all, few humans can tell the difference
between 16 and 100 microseconds.

Although the MOVE routine should be used to copy the contents of one integer variable to
another, copying a character variable is best handled with the LDA/STA sequence.

If you recall the discussion in the first part of this series, I mentioned that character
variables only require 1 byte of storage. Since the 6502 accumulator is 8 bits wide it can
easily accommodate a character value.

If you want to copy the contents of character variable CH1 to character variable CH2 you
should use the code:

      LDA CH1
      STA CH2

While on the subject of character and integer variables, I should mention two functions in
Basic that everyone seems to love: CHR$ and ASC.

SPEED/ASM has no equivalent for these functions because they aren’t needed.

The 6502 treats everything as an integer. Only the programmer distinguishes between
character and numeric data.

The SPEED/ASM program segment in Listing 1 demonstrates this relationship.
It prints the string C195L on the screen. Since the high order bit of all character
values is set, characters are represented by the decimal values 128-255.

As you can see, the interpretation of data stored in the 6502’s memory space is left to
the user.

At any one time the value in a memory cell could contain character, numeric, Boolean or
some other data representation.

The CMP Instruction
The CMP instruction is used to compare the accumulator to a memory location or to a constant.
It affects bits within another register inside the 6502 chip called the processor status
register (or PSR).

The exact definition of these bits is unimportant for now.
What is important is that these bits can be tested with a set of 6502 branch instructions.

The CMP instruction, combined with the branch instructions,
   
   simulates the IF.. THEN. ELSE statements found in high level languages.

To compare the accumulator to a constant value, use the pound sign or slash operator after
the CMP instruction.

A code segment to compare the value in CHR to the character value X is:

      LDA CHR
      CMP #"X"

This fetches the value in CHR, loads it into the accumulator and then compares the
accumulator to the character X.

To compare two character variables load the accumulator with the first and compare it to
the second by specifying the address of the second after the CMP instruction —

  for example:

      LDA CHR1
      CMP CHR2

What you do after a comparison is next on the list...

The Branch and JMP Instructions
Once a comparison is performed, a program typically wants to execute one section of code
if the condition was met, and execute a second section (or just skip over the first)
if the condition was not met.

Transfer of control after a CMP instruction is handled by the branch instructions on the 6502.

"JMP unconditionally transfers control to the address specified in the operand field—almost
exactly like the GOTO statement in Basic."

The first instruction to consider, however, is not a branch at all,
but the JMP (jump) instruction.

JMP unconditionally transfers control to the address specified in the operand field —
almost exactly like the GOTO statement in Basic.

The only difference is that you must specify a statement label rather than a line number
in the operand field.

The following program segment continually writes the character A to the screen:

   LOOP LDA #"A"
        JSR PUTC
        JMP LOOP

The branch instructions transfer control to a target label if and only if a certain
condition exists.

Although, under certain circumstances, a branch instruction may appear almost anywhere in
a program, for now you should only place branch instructions immediately after a CMP instruction.

The BEQ/BNE Instructions
The BEQ (branch if equal) and BNE (branch if not equal) instructions are used to test for
equality and inequality.

If a BEQ instruction follows a compare instruction, then control is transferred to the
target label if the accumulator equals the value it was compared to.

If the value in the accumulator is not equal to the value specified after the CMP instruction,
then the BEQ instruction is ignored and control is transferred to the next instruction
sequentially following the BEQ instruction in the source file.

The program in Listing 2 reads a character from the keyboard and prints it until
a carriage return is read.


             IF THIS BRANCH     USE THIS
             IS OUT OF RANGE... CODE INSTEAD.

             BEQ DEST ;(BFL)      BNE L1   (BTR)
                                  JMP DEST
                                L1:

             BNE DEST ;(BTR)      BEQ L2   (BFL)
                                  JMP DEST
                                L2:

             BMI DEST             BPL L3
                                  JMP DEST
                                L3:

             BPL DEST             BMI L4
                                  JMP DEST
                                L4:

             BCS DEST ;(BGE)      BCC L5
                                  JMP DEST
                                L5:

             BCC DEST ;(BLT)      BCS L6
                                  JMP DEST
                                L6:

             BVS DEST             BVC L7
                                  JMP DEST
                                L7:

             BVC DEST             BVS L8
                                 JMP DEST
                               L8:

       Table 1. Long branch code sequences.

The BNE instruction branches to the target location if the contents of the accumulator is
not equal to the value being compared to.

The BCS (BGE)/BCC (BLT) Instructions
The BCS (branch if carry set) and BCC (branch if carry clear) instructions are used after
a comparison to see if the value in the accumulator is greater than or equal to the value
in the CMP operand field, or if the accumulator is less than the CMP operand.

As a mnemonic aid, the LISA assembler lets you substitute BGE and BLT
(branch if greater than or equal and branch if less than) for BCS and BCC.

The BVS and BVC Instructions
The BVS instruction branches to the target label if the 6502 overflow flag is set.
The BVC instruction branches to the specified location if the 6502 overflow flag is clear.

The overflow flag is set and cleared by arithmetic operations in the 6502.
Certain SPEED/ASM routines also pass an error status back in the overflow flag.

The BVS or BVC instruction can be used after a call to such a routine to test for an error.

The addition and subtraction instructions set or clear the overflow flag depending on the
status of the result produced.

If an overflow occurred, then the overflow flag is set; otherwise it is cleared.

The BMI and BPL Instructions
Unlike the branches discussed up to this point,
the BMI (branch if minus) and BPL (branch if plus) instructions aren’t usually executed
after a CMP instruction. They should be used after an LDA instruction.
BMI specifies a branch if bit 7 of the value in the accumulator is 1;
BPL branches if bit 7 of the accumulator is 0.

To test an integer quantity to see if it is positive or negative use the following code:

      LDA INT+1
      BMI ISNEG

   or

      LDA INT+1
      BPL ISPOS

Note that you must test the high order byte of the integer quantity.
Bit. number 7 of the high order byte is the sign bit for an integer quantity in SPEED/ASM.

Suppose you wanted to set an integer variable negative.

As discussed last time, SPEED/ASM supports two routines for changing the sign of an
integer variable:

ABS and NEG. ABS takes the absolute value of an integer variable.
After execution the specified variable will contain a positive value.

The NEG routine negates (that is, changes the sign of) the specified variable.
If the variable originally contained a positive value, the result will be negative;
if the variable contained a negative value, the result will be positive.

What happens if you want to ensure that a variable contains a negative value regardless of
its original sign?

This problem could easily be handled using two SPEED/ASM calls:

      JSR ABS
      ADR VARIABLE
      JSR NEG
      ADR VARIABLE

The first SPEED/ASM call, to ABS, makes sure that VARIABLE contains a positive number.
The second JSR, to NEG, negates this positive number to yield a negative number.

Although this sequence is clever, a better way to accomplish the task is the following:

                  LDA VARIABLE +1
                  BMI ISNEGTV
                  JSR NEG
                  ADR VARIABLE
      ISNEGTV:

This code checks VARIABLE to see if it is negative
(by loading the high order byte of VARIABLE and branching if it is negative),
and branches around the call to NEG if VARIABLE is already negative.

If VARIABLE is positive NEG negates it.

The BTR and BFL Instructions
BTR (branch if true) and BFL (branch if false) are actually synonyms for the BNE and BEQ
instructions (respectively), LISA 2.5 emits the same object code for BTR and BNE;
likewise the same opcode is emitted for BFL as for BEQ.

The SPEED/ASM package uses the value 0 to represent false and 1 to represent true.
After a value is loaded into the accumulator, the BTR and BFL instructions can test
whether or not the value is zero (false). If so, BFL branches to the specified location.
If not, BTR transfers control to another specified location.

BFL and BTR are used extensively by SPEED/ASM IF routines (to be discussed in a future instalment).

There is one problem with the branch instructions that I haven't mentioned yet:

the branch range is somewhat limited. A 6502 branch instruction uses a special addressing
mode called the relative addressing mode, in which the branch opcode is followed by a
single address byte.

Such a 2-byte instruction saves some memory (normally 3 bytes would be required)
but branches can jump only to a location within a 256-byte range centered at the instruction
following the branch instruction.

Therefore, a branch instruction can branch 129 bytes forward
(from the beginning of the branch instruction) or 126 bytes backwards.

Normally branches occur within this range and there’s no problem.
Occasionally, however, a program needs to branch to a location outside this + 129/ — 126 byte range.

To accomplish this use the opposite-type branch to jump around a 6502 JMP instruction to
the intended address.

For example, if you want to branch if true to location ISTRUE but the assembler gives you
a branch-out-of-range error, substitute the following code:

                    BFL ISNTTRUE
                    JMP TRUE
      ISNTTRUE:

Table 1 lists the instruction sequences to use if a branch-out-of-range error occurs.

Working with Character Variables Through SPEED/ASM
This subtitle may seem somewhat of a misnomer, since the SPEED/ASM package provides
absolutely no character handling routines.

All character handling must be performed by pure 6502 code.

However, character variables are still, in essence, manipulated.

Declaring character variables was described in part 1 of this series.
As a review, to reserve space for a character variable you specify the name of the variable
followed by a pseudo opcode that reserves at least 1 byte of storage.

I usually use the DFS instruction to reserve a single byte.

For example, to reserve 1 byte for a variable named CHAR use the definition:

      CHAR DFS 1

This statement instructs LISA to reserve 1 byte at the current location for the variable CHAR.

As is the case with integer variables, character variables must be defined at a point in
your code where they will not get executed as code.

The best place to put variable definitions is after the JMP EXIT that terminates the program.

Initializing a character variable with a constant was briefly discussed earlier in this article.
To do so load the accumulator with the desired constant
(using the immediate addressing mode — that is, preface the character constant with a pound sign)
and store the accumulator into the character variable.

For example, to load CHAR with the character constant ?

  execute the code:

      LDA #"?"
      STA CHAR

Incidentally, any value can be loaded into CHAR.
It doesn’t have to be a character constant.
You could initialize CHAR to contain the carriage return character by using the code:

      LDA #CR     ;CR is defined in SPEED/ASM.EQUATES
      STA CHAR

Any other numeric or symbol value could be loaded into CHAR in a similar fashion.

Copying one character variable to another (also previously mentioned) is a trivial exercise.
Load the accumulator with the source variable and then store the accumulator into the
destination variable.

To copy CHAR1 into CHAR2 you could use the code:

      LDA CHAR1
      STA CHAR2

I mentioned before that the CHR$ and ASC functions are handled by simple loads and stores.
In certain cases this is true. But if you’re translating Applesoft code into SPEED/ASM you
will run into a few problems.

Most noticeably, Applesoft uses a version of the ASCII character set wherein the ASCII codes
occupy the range 0-127.

To truly implement the CHR$ and ASC functions you should use the code sequences in Listing 3.

The AND and ORA are special 6502 logical instructions that I'll describe in a future article.
For now just copy this code sequence verbatim any time you want to simulate a CHR$ or ASC function.

Comparing character variables is simply a matter of loading one character value into the
accumulator using the LDA instruction and comparing it to another using the CMP instruction.
This month’s sample program (Listing 4) demonstrates several character variable manipulations
that should help answer any questions you have about character handling.

Notes of a Commercial Interest
The LISA assembler and the SPEED/ASM programming package are available from your local
computer store and many mail order software houses.

If you cannot locate a copy of either of these packages, you can order directly from
Sierra On-Line Inc., 36575 Mudge Ranch Road, Coarsegold, CA 93614.
The LISA assembler, SPEED/ASM, and Datamost’s Using 6502 Assembly Language book are all
available in a special LISA Educational Package available where LISA is sold.
